/**
 * @file es_def.h
 *
 * @brief Define some commonly used macro definitions.
 *
 * @details This file implements macro definitions for obtaining array elements, connecting strings, setting, clearing,
 * 			inverting a certain bit, etc.
 *
 * @author bagy
 *
 * @version V1.0
 *
 * @date 2024-05-14
 */

#ifndef  __ES_DEF_H__
#define  __ES_DEF_H__

#include "es_type.h"
#include "es_assert.h"

/**
 * @brief This macro is used for incapacitation marking.
 */
#define ES_DISABLE                        (0)

/**
 * @brief This macro is used to enable the marking.
 */
#define ES_ENABLE                         (!ES_DISABLE)

/**
 * @brief The function of this macro is to count the number of array elements.
 *
 * @param _array Array with number of elements to be calculated.
 *
 * @return The number of elements in the array.
 */
#define ES_ARRAY_SIZE(_array)            (sizeof(_array) / sizeof(_array)[0])

/**
 * @brief The function of this macro is to connect the two input strings
 *
 * @param _a First string
 * @param _b Second string
 *
 * @return The concatenated string
 */
#define ES_CONCAT(_a, _b)                   _a##_b

/**
 * @brief The function of this macro is to convert integer data into string
 *
 * @param _a Integer to be converted
 *
 * @return Converted string
 */
#define ES_STR(_a)                          #_a

/**
 * @brief This macro converts an ASCII character representing a hexadecimal digit to its corresponding hexadecimal value.
 *
 * @param _c The ASCII character to convert.
 * @return The hexadecimal value represented by the ASCII character, or -1 if the input is not a valid hexadecimal character.
 */
#define ES_ASCII_TO_HEX(_c)  	            ((_c) >= '0' && (_c) <= '9' ? (_c) - '0' : ((_c) >= 'A' && \
										    (_c) <= 'F' ? (_c) - 'A' + 10 : ((_c) >= 'a' && (_c) <= 'f' ? (_c) - 'a' + 10 : -1)))

/**
 * @brief This macro converts a hexadecimal value to its corresponding ASCII character.
 *
 * @param _hex The hexadecimal value (0-15) to convert.
 * @return The ASCII character representing the hexadecimal value.
 */
#define ES_HEX_TO_ASCII(_hex)  	            ((_hex) < 10 ? (_hex) + '0' : (_hex) + 'A' - 10)

/**
 * @brief Macro function to get the state of a specific bit in a uint8_t value.
 *
 * @param _value The uint8_t value from which to get the bit.
 * @param _bit The index of the bit to retrieve (0 to 7).
 *
 * @return 1 if the specified bit is set (1), otherwise 0.
 */
#define ES_GET_BIT(_value, _bit)            (((_value) >> (_bit)) & 1)

/**
 * @brief This macro function checks if a value is set.
 *
 * @param _value The value to check.
 * @param _mask The mask to check.
 */
#define ES_BIT_IS_SET(_value, _mask)        (((_value) & (_mask)) == (_mask))

/**
 * @brief This macro function checks if a value is clear.
 *
 * @param _value The value to check.
 * @param _mask The mask to check.
 */
#define ES_BIT_IS_CLEAR(_value, _mask)      (((_value) & (1 << _mask)) == 0)

/**
 * @brief This macro function is sets a value.
 *
 * @param _value The value to set.
 * @param _mask The mask to set.
 */
#define ES_BIT_SET(_value, _mask)           ((_value) |= (_mask))

/**
 * @brief This macro function is clears a value.
 *
 * @param _value The value to clear.
 * @param _mask The mask to clear.
 */
#define ES_BIT_CLR(_value, _mask)           ((_value) &= ~(_mask))

/**
 * @brief This macro function is toggles a value.
 *
 * @param _value The value to toggle.
 * @param _mask The mask to toggle.
 */
#define ES_BIT_TOGGLE(_value, _mask)        ((_value) ^= (_mask))

/**
 * @brief This macro function converts a lowercase letter to its uppercase equivalent.
 *
 * @param _ch The lowercase character to convert.
 */
#define ES_TO_UPPER(_ch)                    ((_ch) - 'a' + 'A')
    
/**
 * @brief This macro function checks if a character is an uppercase letter.
 *
 * @param _ch The character to check.
 * @return True if _ch is an uppercase letter, false otherwise.
 */
#define ES_IS_UPPER(_ch)                    (((_ch) >= 'A') && ((_ch) <= 'Z'))

/**
 * @brief This macro function checks if a character is a lowercase letter.
 *
 * @param _ch The character to check.
 * @return True if _ch is a lowercase letter, false otherwise.
 */
#define ES_IS_LOWER(_ch)                    (((_ch) >= 'a') && ((_ch) <= 'z'))

/**
 * @brief This macro function checks if a character is a letter (either uppercase or lowercase).
 *
 * @param _ch The character to check.
 * @return True if _ch is a letter, false otherwise.
 */
#define ES_IS_LETTER(_ch)                   (ES_IS_UPPER(_ch) || ES_IS_LOWER(_ch))

/**
 * @brief This macro function limits the maximum value of a variable.
 *
 * @param _val The value to limit.
 * @param _max The maximum allowed value.
 * @return The limited value.
 */
#define ES_LIMIT_MAX(_val, _max)            (((_val) > (_max)) ? (_max) : (_val))   

/**
 * @brief This macro function limits the minimum value of a variable.
 *
 * @param _val The value to limit.
 * @param _min The minimum allowed value.
 * @return The limited value.
 */
#define ES_LIMIT_MIN(_val, _min)            (((_val) < (_min)) ? (_min) : (_val))

/**
 * @brief This macro function limits a variable to a range by setting both minimum and maximum values.
 *
 * @param _val The value to limit.
 * @param _min The minimum allowed value.
 * @param _max The maximum allowed value.
 * @return The limited value.
 */
#define ES_LIMIT_RANGE(_val, _min, _max)    (ES_LIMIT_MAX(ES_LIMIT_MIN(_val, _max), _min))

/**
 * @brief The value of Pi.
 */
#define ES_PI 	                            (double)(3.14159265358979323846)

/**
 * @brief Converts angle to radians.
 * @param _ang The angle value to convert.
 * @return The radian value.
 */
#define ES_DEG_TO_RAD(_ang)                 ((_ang) * (ES_PI / (double)180.0))

/**
 * @brief Converts radians to angle.
 * @param _rad The radian value to convert.
 * @return The angle value.
 */
#define ES_RAD_TO_DEG(_rad)                 ((_rad) * ((double)180.0 / ES_PI))
    
/**
 * @brief Checks if a number is odd.
 * @param _num The number to check.
 * @return 1 if _num is odd, 0 otherwise.
 */
#define ES_IS_ODD(_num)                     ((_num) % 2)

/**
 * @brief Checks if a number is even.
 * @param _num The number to check.
 * @return 1 if _num is even, 0 otherwise.
 */
#define ES_IS_EVEN(_num)                    (!(_num) % 2)

/**
 * @brief Converts a floating-point number to a hexadecimal representation.
 * @param _f The floating-point number to convert.
 * @return The hexadecimal representation.
 */
#define ES_FLOAT_TO_HEX(_f)                 (*(unsigned int *)&_f)

/**
 * @brief Converts a hexadecimal representation back to a floating-point number.
 * @param _ui The hexadecimal representation to convert.
 * @return The floating-point number.
 */
#define ES_HEX_TO_FLOAT(_ui)                (*(float *)&_ui)

/**
 * @brief Converts a BCD value to a Decimal value.
 *
 * This macro converts a Binary-Coded Decimal (BCD) value to a Decimal value.
 * 
 * Example:
 *   uint8_t bcd = 0x25; // BCD representation of 25
 *   uint8_t dec = ES_BCD_TO_DEC(bcd); // dec will be 25
 *
 * @param _bcd The BCD value to convert.
 * @return The converted Decimal value.
 */
#define ES_BCD_TO_DEC(_bcd)                 ((((_bcd) >> 4) * 10) + ((_bcd) & 0x0F))

/**
 * @brief Converts a Decimal value to a BCD value.
 *
 * This macro converts a Decimal value to a Binary-Coded Decimal (BCD) value.
 * 
 * Example:
 *   uint8_t dec = 25; // Decimal value 25
 *   uint8_t bcd = ES_DEC_TO_BCD(dec); // bcd will be 0x25
 *
 * @param _dec The Decimal value to convert.
 * @return The converted BCD value.
 */
#define ES_DEC_TO_BCD(_dec)                 ((((_dec) / 10) << 4) | ((_dec) % 10))

/**
 * @brief First-order low-pass filter algorithm.
 * @param _value The new input value.
 * @param _prev The previous output value.
 * @param _alpha The smoothing factor (0 < alpha < 1).
 * @return The filtered value.
 */
#define ES_LOW_PASS_FILTER_1ST(_value, _prev, _alpha)       ((_value) + (_alpha) * (_prev - (_value)))
    
/**
 * @brief This macro function checks if a point is inside a rectangle.
 *
 * @param _x The x-coordinate of the point to check.
 * @param _y The y-coordinate of the point to check.
 * @param _xMin The minimum x-coordinate of the rectangle.
 * @param _yMin The minimum y-coordinate of the rectangle.
 * @param _xMax The maximum x-coordinate of the rectangle.
 * @param _yMax The maximum y-coordinate of the rectangle.
 * @return True if the point is inside the rectangle, false otherwise.
 */
#define ES_IS_IN_RECT(_x, _y, _xMin, _yMin, _xMax, _yMax)   ((_x) >= (_xMin) && (_x) <= (_xMax) && (_y) >= (_yMin) && (_y) <= (_yMax))
    
/**
 * @brief This macro function checks if a point is inside a circle.
 *
 * @param _x The x-coordinate of the point to check.
 * @param _y The y-coordinate of the point to check.
 * @param _cx The x-coordinate of the circle's center.
 * @param _cy The y-coordinate of the circle's center.
 * @param _radius The radius of the circle.
 * @return True if the point is inside the circle, false otherwise.
 */
#define ES_IS_IN_CIRCLE(_x, _y, _cx, _cy, _radius)          ((((_x) - (_cx)) * ((_x) - (_cx)) + ((_y) - ((_cy))) * ((_y) - ((_cy)))) <= (_radius) * (_radius))
   
/**
 * @brief Maps a value from one range to another.
 * @param _x The value to map.
 * @param _in_min The minimum value of the input range.
 * @param _in_max The maximum value of the input range.
 * @param _out_min The minimum value of the output range.
 * @param _out_max The maximum value of the output range.
 * @return The mapped value.
 */
#define ES_MAP(_x, _in_min, _in_max, _out_min, _out_max)    ((_x) * ((_out_max) - (_out_min)) / ((_in_max) - (_in_min)) + (_out_min))


/**
 * @brief Converts a total number of seconds into hours, minutes, and seconds.
 * @param _time_cnt The total number of seconds.
 * @param hour A pointer to store the hour value.
 * @param min A pointer to store the minute value.
 * @param sec A pointer to store the second value.
 */
#define ES_CONVERT_SEC_TO_TIME(_time_cnt, hour, min, sec)   do { \
                                                                    *(hour) = (_time_cnt) / 3600; \
                                                                    _time_cnt %= 3600; \
                                                                    *(min) = (_time_cnt) / 60; \
                                                                    *(sec) = (_time_cnt) % 60; \
                                                                } while (0)
    
/**
 * @brief Converts hours, minutes, and seconds into a total number of seconds.
 * @param _hour The number of hours.
 * @param _min The number of minutes.
 * @param _sec The number of seconds.
 * @return The total number of seconds.
 */
#define ES_TIME_TO_SEC(_hour, _min, _sec)   ((_hour) * 3600 + (_min) * 60 + (_sec))

 /**
 * @brief The function of this macro is to compare the sizes of two numbers and determine whether they are of the same data type.
 *
 * @param _a The first number to be compared.
 * @param _b The second number to be compared.
 *
 * @return Returns the larger of the two input parameters.
 */
#define ES_MAX(_a, _b)       		        ({                     \
                           	   	   	   	        typeof(a) _a = (a);  \
                           	   	   	   	        typeof(b) _b = (b);  \
                           	   	   	   	        (ES_VOID) (&_a == &_b); \
                           	   	   	   	        _a > _b ? _a : _b; })

/**
 * @brief The function of this macro is to compare the sizes of two numbers and determine whether they are of the same data type.
 *
 * @param _a The first number to be compared.
 * @param _b The second number to be compared.
 *
 * @return Returns the smaller of the two input parameters.
 */
#define ES_MIN(_a, _b)       		        ({                         \
                           	   	   	   	        typeof(a) _a = (a);    \
                           	   	   	   	        typeof(b) _b = (b);    \
                           	   	   	   	        (ES_VOID) (&_a == &_b);\
                           	   	   	   	        _a < _b ? _a : _b; })

/**
 * @brief This macro function checks if a value is within a specified range.
 *
 * @param _a The value to check.
 * @param _min The minimum value of the range.
 * @param _max The maximum value of the range.
 *
 * @return 1 if the value is within the range (inclusive), 0 otherwise.
 */
#define ES_IN_RANGE(_a, _min, _max)         ((_a) >= (_min) && (_a) <= (_max))

/**
 * @brief Checks if a floating-point number is close enough to zero using a default epsilon.
 *
 * This macro uses a default epsilon value of 1e-6 to determine if the floating-point number
 * is effectively zero. This is useful for comparing floating-point numbers in calculations
 * where exact zero may not be representable due to precision limitations.
 *
 * @param _num The floating-point number to check.
 * @return 1 if _num is close enough to zero, 0 otherwise.
 */
#define ES_IS_FLOAT_ZERO(_num)              (fabs(_num) < 1e-6)

/**
 * @brief This macro function swaps two values.
 *
 * @param _a The first value to swap.
 * @param _b The second value to swap.
 */
#define ES_SWAP(_a, _b)                     do {                           \
							                        (ES_VOID) (&_a == &_b);\
                                                    typeof(_a) _tmp = (_a);\
                                                    (_a) = (_b);           \
                                                    (_b) = _tmp;           \
                                                } while (0)

/**
 * @brief This macro function is creates a local variable.
 *
 * @param _type The type of the variable.
 * @param ... The arguments.
 *
 * @return A pointer to the variable.
 *
 * @note The variable is local, please use it carefully.
 */
#define ES_MAKE_LOCAL(_type, ...)           (&((_type){__VA_ARGS__}))

/**
 * @brief This macro function converts a value to a boolean.
 *
 * @param _value The value to convert.
 *
 * @return The boolean value.
 */
#define ES_TO_BOOL(_value)                  (!!(_value))

/**
 * @brief This macro retrieves the low 8 bits of a 16-bit unsigned integer.
 *
 * @param x The 16-bit unsigned integer.
 * @return The low 8 bits of the input.
 */
#define ES_GET_U16_LOW(_x)   			    ((_x) & 0xFF)

/**
 * @brief This macro retrieves the high 8 bits of a 16-bit unsigned integer.
 *
 * @param x The 16-bit unsigned integer.
 * @return The high 8 bits of the input.
 */
#define ES_GET_U16_HIGH(_x)                 (((_x) >> 8) & 0xFF)

/**
 * @brief This macro retrieves the lowest 8 bits (byte 0) of a 32-bit unsigned integer.
 *
 * @param x The 32-bit unsigned integer.
 * @return The lowest 8 bits of the input.
 */
#define ES_GET_U32_BYTE0(_x)                ((_x) & 0xFF)

/**
 * @brief This macro retrieves the second lowest 8 bits (byte 1) of a 32-bit unsigned integer.
 *
 * @param x The 32-bit unsigned integer.
 * @return The second lowest 8 bits of the input.
 */
#define ES_GET_U32_BYTE1(_x)                (((_x) >> 8) & 0xFF)

/**
 * @brief This macro retrieves the third lowest 8 bits (byte 2) of a 32-bit unsigned integer.
 *
 * @param x The 32-bit unsigned integer.
 * @return The third lowest 8 bits of the input.
 */
#define ES_GET_U32_BYTE2(_x)                (((_x) >> 16) & 0xFF)

/**
 * @brief This macro retrieves the highest 8 bits (byte 3) of a 32-bit unsigned integer.
 *
 * @param x The 32-bit unsigned integer.
 * @return The highest 8 bits of the input.
 */
#define ES_GET_U32_BYTE3(_x)                (((_x) >> 24) & 0xFF)

/**
 * @brief This macro combines two 8-bit unsigned integers into a 16-bit unsigned integer.
 *
 * @param high The high 8 bits.
 * @param low The low 8 bits.
 * @return The combined 16-bit unsigned integer.
 */
#define ES_MAKE_U16(_high, _low)            (((ES_U16)(_high) << 8) | (ES_U16)(_low))

/**
 * @brief This macro combines four 8-bit unsigned integers into a 32-bit unsigned integer.
 *
 * @param byte1 The highest 8 bits.
 * @param byte2 The second highest 8 bits.
 * @param byte3 The third highest 8 bits.
 * @param byte4 The lowest 8 bits.
 * @return The combined 32-bit unsigned integer.
 */
#define ES_MAKE_U32(_b1, _b2, _b3, _b4)     (((ES_U32)(_b1) << 24) | ((ES_U32)(_b2) << 16) | ((ES_U32)(_b3) << 8) | (ES_U32)(_b4))

/**
 * @brief This macro swaps the high and low bytes of a 16-bit unsigned integer.
 *
 * @param _val The 16-bit unsigned integer.
 * @return The value with high and low bytes swapped.
 */
#define ES_SWAP_U16_BYTES(_val)             ((((_val) & 0xFF) << 8) | (((_val) >> 8) & 0xFF))

/**
 * @brief This macro swaps the byte order of a 32-bit unsigned integer.
 *
 * @param _val The 32-bit unsigned integer.
 * @return The value with byte order swapped.
 */
#define ES_SWAP_U32_BYTES(_val)             ((((ES_U32)(_val) & 0xFF) << 24) |       \
										    ((((ES_U32)(_val) >> 8) & 0xFF) << 16) | \
										    ((((ES_U32)(_val) >> 16) & 0xFF) << 8) | \
										    (((ES_U32)(_val) >> 24) & 0xFF))

/**
 * @brief This macro writes a 32-bit unsigned integer to the specified memory address.
 *
 * @param _addr The memory address to write to.
 * @param _val The 32-bit unsigned integer to write.
 */
#define ES_WRITE_REG32(_addr, _val)         (*((ES_U32*)(_addr)) = (_val))

/**
 * @brief This macro reads a 32-bit unsigned integer from the specified memory address.
 *
 * @param _addr The memory address to read from.
 * @return The 32-bit unsigned integer read from the address.
 */
#define ES_READ_REG32(_addr)                (*((ES_U32*)(_addr)))

/**
 * @brief This macro writes a 16-bit unsigned integer to the specified memory address.
 *
 * @param _addr The memory address to write to.
 * @param _val The 16-bit unsigned integer to write.
 */
#define ES_WRITE_REG16(_addr, _val)         (*((ES_U16*)(_addr)) = (_val))

/**
 * @brief This macro reads a 16-bit unsigned integer from the specified memory address.
 *
 * @param _addr The memory address to read from.
 * @return The 16-bit unsigned integer read from the address.
 */
#define ES_READ_REG16(_addr)                (*((ES_U16*)(_addr)))

/**
 * @brief This macro writes an 8-bit unsigned integer to the specified memory address.
 *
 * @param _addr The memory address to write to.
 * @param _val The 8-bit unsigned integer to write.
 */
#define ES_WRITE_REG8(_addr, _val)          (*((ES_U8*)(_addr)) = (_val))

/**
 * @brief This macro reads an 8-bit unsigned integer from the specified memory address.
 *
 * @param _addr The memory address to read from.
 * @return The 8-bit unsigned integer read from the address.
 */
#define ES_READ_REG8(_addr)                 (*((ES_U8*)(_addr)))

/**
 * @brief Calculates the offset of a member within a structure.
 *
 * @param TYPE The type of the structure.
 * @param MEMBER The member whose offset is to be calculated.
 * @return The offset of the member within the structure.
 *
 * @details This macro calculates the offset of a member within a structure.
 * It returns the byte offset of the given member within the specified structure type.
 * This macro is commonly used in low-level programming for implementing data structures
 * or when dealing with hardware registers to determine the memory layout of structures.
 */
#define ES_OFFSETOF(TYPE, MEMBER)           ((size_t) &((TYPE *)0)->MEMBER)

/**
 * @brief Obtains the address of the container structure from a member pointer.
 *
 * @param ptr A pointer to the member.
 * @param type The type of the container structure.
 * @param member The name of the member within the container structure.
 * @return A pointer to the container structure.
 *
 * @details This macro retrieves the address of the container structure from a pointer to one of its members.
 * It takes a pointer to the member, the type of the container structure, and the name of the member
 * within the container structure as parameters. It then calculates the address of the container structure
 * based on the pointer to the member and the offset of the member within the structure.
 * This macro is often used in container-based programming, such as implementing linked lists,
 * trees, or other data structures where elements contain pointers to their enclosing structures.
 */
#define ES_CONTAINER_OF(ptr, type, member)  ({ES_CONST typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        								   	   (type *)( (ES_INT8 *)__mptr - offsetof(type,member) );})

/**
 * @addtogroup Auto-Init
 * @{
 */

/** Auto-initialization functions */
typedef void (*es_init_fn_t)(void);
/** Exports an initialization function by level */
#define ES_INIT_EXPORT(_fn, _level)          es_init_fn_t _es_init_##_fn __attribute__((used, section(".es_init." _level))) = _fn
/** Exports a Board initialization function */
#define ES_INIT_BOARD_EXPORT(_fn)       	 ES_INIT_EXPORT(_fn, "1")
/** Exports a Driver initialization function */
#define ES_INIT_DRIVER_EXPORT(_fn)      	 ES_INIT_EXPORT(_fn, "2")
/** Exports a Device initialization function */
#define ES_INIT_DEVICE_EXPORT(_fn)      	 ES_INIT_EXPORT(_fn, "3")
/** Exports an App initialization function */
#define ES_INIT_APP_EXPORT(_fn)         	 ES_INIT_EXPORT(_fn, "4")

 /** @} */

#define ES_EOK                          	 (0)                 /** No error */
#define ES_EPERM                        	 (-1)                /** Operation not permitted */
#define ES_ENOENT                       	 (-2)                /** No such file or directory */
#define ES_EIO                          	 (-3)                /** I/O error */
#define ES_ENOMEM                       	 (-4)                /**< Out of memory */
#define ES_EACCES                        	 (-5)                /**< Permission denied */
#define ES_EBUSY                         	 (-6)                /**< Resource busy */
#define ES_EEXIST                       	 (-7)                /**< Resource exists */
#define ES_EINVAL                       	 (-8)                /**< Invalid argument */
#define ES_ETIMEOUT                     	 (-9)                /**< Operation timed */

#endif   /* __ES_DEF_H__ */
